| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- import { useParams } from 'common'
- import { ChevronDown, FolderOpen, Settings, Shield, Trash2 } from 'lucide-react'
- import Link from 'next/link'
- import { useRouter } from 'next/router'
- import { parseAsBoolean, useQueryState } from 'nuqs'
- import { useEffect } from 'react'
- import { toast } from 'sonner'
- import {
- Badge,
- Button,
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuSeparator,
- DropdownMenuTrigger,
- Tooltip,
- TooltipContent,
- TooltipTrigger,
- } from 'ui'
- import { DeleteBucketModal } from '@/components/interfaces/Storage/DeleteBucketModal'
- import { EditBucketModal } from '@/components/interfaces/Storage/EditBucketModal'
- import { EmptyBucketModal } from '@/components/interfaces/Storage/EmptyBucketModal'
- import { useSelectedBucket } from '@/components/interfaces/Storage/FilesBuckets/useSelectedBucket'
- import { PublicBucketWarning } from '@/components/interfaces/Storage/PublicBucketWarning'
- import { PUBLIC_BUCKET_TOOLTIP } from '@/components/interfaces/Storage/Storage.constants'
- import StorageBucketsError from '@/components/interfaces/Storage/StorageBucketsError'
- import { StorageExplorer } from '@/components/interfaces/Storage/StorageExplorer/StorageExplorer'
- import { useBucketPolicyCount } from '@/components/interfaces/Storage/useBucketPolicyCount'
- import DefaultLayout from '@/components/layouts/DefaultLayout'
- import { PageLayout } from '@/components/layouts/PageLayout/PageLayout'
- import StorageLayout from '@/components/layouts/StorageLayout/StorageLayout'
- import { StorageExplorerStateContextProvider } from '@/state/storage-explorer'
- import type { NextPageWithLayout } from '@/types'
- const BucketPage: NextPageWithLayout = () => {
- const router = useRouter()
- const { bucketId, ref } = useParams()
- const { data: bucket, error, isSuccess, isError } = useSelectedBucket()
- const [showEditModal, setShowEditModal] = useQueryState(
- 'edit',
- parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
- )
- const [showEmptyModal, setShowEmptyModal] = useQueryState(
- 'empty',
- parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
- )
- const [showDeleteModal, setShowDeleteModal] = useQueryState(
- 'delete',
- parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
- )
- const { getPolicyCount } = useBucketPolicyCount()
- const policyCount = bucket ? getPolicyCount(bucket.id) : 0
- useEffect(() => {
- if (isSuccess && !bucket) {
- toast.info(`Bucket "${bucketId}" does not exist in your project`)
- router.push(`/project/${ref}/storage/files`)
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [isSuccess])
- if (isError) {
- return <StorageBucketsError error={error} />
- }
- return (
- <StorageExplorerStateContextProvider key={`storage-explorer-state-${ref}`}>
- <PageLayout
- size="full"
- isCompact
- className="[&>div:first-child]:border-b-0!" // Override the border-b from ScaffoldContainer
- title={
- <div className="flex items-center gap-2 min-w-0 flex-1">
- <span className="truncate">{bucketId}</span>
- {bucket?.public && (
- <Tooltip>
- <TooltipTrigger asChild>
- <Badge variant="warning" className="flex shrink-0">
- Public
- </Badge>
- </TooltipTrigger>
- <TooltipContent side="bottom">{PUBLIC_BUCKET_TOOLTIP}</TooltipContent>
- </Tooltip>
- )}
- </div>
- }
- breadcrumbs={[
- {
- label: 'Files',
- href: `/project/${ref}/storage/files`,
- },
- {
- label: 'Buckets',
- href: `/project/${ref}/storage/files`,
- },
- ]}
- primaryActions={
- <>
- <Button
- asChild
- type="default"
- icon={<Shield size={14} />}
- iconRight={
- policyCount > 0 ? (
- <span className="w-4 h-4 bg-surface-200 text-foreground-light text-xs rounded-full flex items-center justify-center font-medium">
- {policyCount}
- </span>
- ) : undefined
- }
- >
- <Link
- href={`/project/${ref}/storage/files/policies?search=${encodeURIComponent(bucket?.name ?? '')}`}
- >
- Policies
- </Link>
- </Button>
- <DropdownMenu>
- <DropdownMenuTrigger asChild>
- <Button type="default" iconRight={<ChevronDown size={14} />}>
- Edit bucket
- </Button>
- </DropdownMenuTrigger>
- <DropdownMenuContent align="end" className="w-40">
- <DropdownMenuItem
- className="flex items-center space-x-2"
- onClick={() => setShowEditModal(true)}
- >
- <Settings size={12} />
- <p>Bucket settings</p>
- </DropdownMenuItem>
- <DropdownMenuSeparator />
- <DropdownMenuItem
- className="flex items-center space-x-2"
- onClick={() => setShowEmptyModal(true)}
- >
- <FolderOpen size={12} />
- <p>Empty bucket</p>
- </DropdownMenuItem>
- <DropdownMenuItem
- className="flex items-center space-x-2"
- onClick={() => setShowDeleteModal(true)}
- >
- <Trash2 size={12} />
- <p>Delete bucket</p>
- </DropdownMenuItem>
- </DropdownMenuContent>
- </DropdownMenu>
- </>
- }
- >
- <div className="flex-1 min-h-0 px-6 pb-6 flex flex-col gap-4">
- {ref && bucketId && <PublicBucketWarning projectRef={ref} bucketId={bucketId} />}
- <div className="flex-1 min-h-0">
- <StorageExplorer />
- </div>
- </div>
- </PageLayout>
- {bucket && (
- <>
- <EditBucketModal
- visible={showEditModal}
- bucket={bucket}
- onClose={() => setShowEditModal(false)}
- />
- <EmptyBucketModal
- visible={showEmptyModal}
- bucket={bucket}
- onClose={() => setShowEmptyModal(false)}
- />
- <DeleteBucketModal
- visible={showDeleteModal}
- bucket={bucket}
- onClose={() => setShowDeleteModal(false)}
- />
- </>
- )}
- </StorageExplorerStateContextProvider>
- )
- }
- BucketPage.getLayout = (page) => (
- <DefaultLayout>
- <StorageLayout title="Buckets">{page}</StorageLayout>
- </DefaultLayout>
- )
- export default BucketPage
|